package com.young.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.young.config.DirConfiguration;
import com.young.dto.Result;
import com.young.entity.ChunkFile;
import com.young.mapper.ChunkFileMapper;
import com.young.service.ChunkFileService;
import com.young.util.ResultUtil;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.*;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ChunkServiceImpl implements ChunkFileService {
    @Resource
    private ChunkFileMapper chunkFileMapper;
    @Resource
    private DirConfiguration dirConfiguration;
    @Override
    public String splitFile(String sourceFile, String dir, String identifier,int count) {
//        log.info("source:"+dirConfiguration.getSource());
//        log.info("temp:"+dirConfiguration.getTemp());
//        log.info("target:"+dirConfiguration.getTarget());
//        return 0l;
        try{
            RandomAccessFile raf=new RandomAccessFile(new File(sourceFile),"r");
            //计算文件大小
            long length=raf.length();
            //计算切片后的文件大小
            long maxSize=length/count;
            //偏移量
            long offset=0L;
            //开始切割文件
            for (int i=0;i<count-1;i++){
                long fbegin=offset;
                long fend=(i+1)*maxSize;
                //写入文件
                offset=getWrite(sourceFile,dir,identifier,i,fbegin,fend);
            }
            //剩余部分文件写入到最后一份
            if (length-offset>0){
                getWrite(sourceFile,dir,identifier,count-1,offset,length);
            }
        }catch (Exception e){
            e.printStackTrace();
            return "fail";
        }
        return "success";
    }

    @Override
    public String getIdentifier(String filename) {
        try (            FileInputStream fileInputStream=new FileInputStream(filename);
        ){
            return DigestUtils.md5Hex(fileInputStream);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        }catch (Exception e){
            e.printStackTrace();
        }
        return "";
    }

    @Override
    public Result merge(String identifier) {
        LambdaQueryWrapper<ChunkFile>queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(ChunkFile::getIdentifier,identifier);
        List<ChunkFile> chunkFiles = chunkFileMapper.selectList(queryWrapper);
        if (chunkFiles==null||chunkFiles.size()==0){
          return ResultUtil.fail(400,"文件不存在");
        }
        ChunkFile chunkFile=chunkFiles.get(0);
        if (chunkFiles.size()<chunkFile.getTotalChunks()){
            return ResultUtil.fail(400,"文件未上传完毕");
        }
        //获取临时文件的前缀,比如临时文件为123522_1.tmp,那么preTempPath就是123522
        String tempPath=chunkFile.getPath();
        String preTempPath = tempPath.substring(0, tempPath.lastIndexOf("_"));
        String dir=dirConfiguration.getTarget()+identifier;
        File targetFile=new File(dir,identifier+"."+chunkFile.getSuffix());
        if (!targetFile.exists()){
            targetFile.getParentFile().mkdirs();
            try{
                targetFile.createNewFile();
            }catch (Exception e){
                e.printStackTrace();
                return ResultUtil.fail();
            }
        }
        try(            FileOutputStream outputStream=new FileOutputStream(targetFile);
        ){
            FileInputStream inputStream=null;
            byte[]buf=new byte[4*1024];
            int len;
            for (int i=0;i<chunkFiles.size();i++){
                String sourceFilePath=preTempPath+"_"+i+".tmp";
                inputStream=new FileInputStream(new File(sourceFilePath));
                while ((len=inputStream.read(buf))!=-1){
                    outputStream.write(buf,0,len);
                }
            }
        }catch (Exception e){
            e.printStackTrace();
            return ResultUtil.fail();
        }
        //删除临时文件
        deleteTemp(preTempPath,chunkFiles.size());
        return ResultUtil.success();
    }

    private void deleteTemp(String preTempPath, int size) {
        ExecutorService executorService= Executors.newFixedThreadPool(4);
        for (int i=0;i<size;i++){
            int index=i;
            executorService.execute(new Runnable() {
                @Override
                public void run() {
                    String path=preTempPath+"_"+index+".tmp";
                    File file=new File(path);
                    file.delete();
                }
            });
        }
    }

    @Override
    public Result verify(String identifier) {
        //根据identifier获取数据库内容
        LambdaQueryWrapper<ChunkFile>queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(ChunkFile::getIdentifier,identifier);
        List<ChunkFile> chunkFiles = chunkFileMapper.selectList(queryWrapper);
        Map<String,Object>res=new HashMap<>();
        if (chunkFiles==null||chunkFiles.size()==0){
            res.put("complete",false);
            return new Result<>(101,"文件还未上传",res);
        }
        //获取第一个文件
        ChunkFile chunkFileOne = chunkFiles.get(0);
        //获取总数
        Integer totalChunks = chunkFileOne.getTotalChunks();
        if (chunkFiles.size()<totalChunks){
            res.put("complete",false);
            List<Integer> successList = chunkFiles.stream().map(chunkFile -> chunkFile.getChunkIndex())
                    .collect(Collectors.toList());
            res.put("successList",successList);
            return ResultUtil.fail(101,"文件未全部上传",res);
        }
        res.put("complete",true);
        return ResultUtil.success(res);
    }

    @Override
    public Result uploadChunk(MultipartFile file, String identifier,String fileName, String suffix, Integer chunkIndex, Integer chunkTotal, Integer chunkSize,Integer totalSize) {
        //判断文件是否已上传过
        LambdaQueryWrapper<ChunkFile>queryWrapper=new LambdaQueryWrapper<>();
        queryWrapper.eq(ChunkFile::getIdentifier,identifier)
                .eq(ChunkFile::getChunkIndex,chunkIndex);
        ChunkFile chunkFile = chunkFileMapper.selectOne(queryWrapper);
        if (chunkFile!=null){
            return ResultUtil.fail(400,"文件已经上传过");
        }
        chunkFile=new ChunkFile();
        //上传文件
        //文件的目录
        String dir=dirConfiguration.getTarget()+identifier+File.separator;
        String path=dir+identifier+"_"+chunkIndex+".tmp";
        try {
            upload(file,path);
            //存储文件信息到数据库
            chunkFile.setPath(path);
            chunkFile.setFileName(fileName);
            chunkFile.setSuffix(suffix);
            chunkFile.setTotalSize(totalSize);
            chunkFile.setTotalChunks(chunkTotal);
            chunkFile.setCreatedAt(LocalDateTime.now());
            chunkFile.setChunkIndex(chunkIndex);
            chunkFile.setChunkSize(chunkSize);
            chunkFile.setIdentifier(identifier);
            chunkFileMapper.insert(chunkFile);
        } catch (IOException e) {
            e.printStackTrace();
            return ResultUtil.fail();
        }
        return ResultUtil.success();
    }

    private void upload(MultipartFile file, String path) throws IOException {
        File targetFile=new File(path);
        if (!targetFile.exists()){
            targetFile.getParentFile().mkdirs();
            targetFile.createNewFile();
        }
        FileOutputStream outputStream=new FileOutputStream(targetFile);
        InputStream inputStream = file.getInputStream();
        byte[]buf=new byte[1024*4];
        int len;
        while ((len=inputStream.read(buf))!=-1){
            outputStream.write(buf,0,len);
        }
        if (outputStream!=null){
            outputStream.close();
        }
        if (inputStream!=null){
            inputStream.close();
        }
    }

    private long getWrite(String file,String dir,String identifier,int index,long begin,long end){
        long endPointer=0L;
        try( RandomAccessFile in=new RandomAccessFile(new File(file),"r");
             RandomAccessFile out=new RandomAccessFile(new File(dir+identifier+"_"+index+".tmp"),"rw");){
            byte[]b=new byte[1024];
            int n=0;
            in.seek(begin);
            while (in.getFilePointer()<=end&&(n=in.read(b))!=-1){
                out.write(b,0,n);
            }
            endPointer=in.getFilePointer();
        }catch (Exception e){
            e.printStackTrace();
        }
        return endPointer;
    }
}
